#pregma once

#include <iostream>
#include <string>
#include <queue>
#include <unistd.h>
#include <pthread.h>

namespace ns_threadpool
{
    template<class T>
    class ThreadPool
    {
    private:
        int num_;
        std::queue<T> task_queue_;
        
        pthread_mutex_t mtx_;
        pthread_cond_t cond_;

    public:
        void Lock()
        {
            pthread_mutex_lock(&mtx_);
        }

        void Unlock()
        {
            pthread_mutex_unlock(&mtx_);
        }

        void Wait()
        {
            pthread_cond_wait(&cond_,mtx_);
        }

        void Wakeup()
        {
            pthread_cond_signal(&cond_);
        }

        bool IsEmpty()
        {
            return task_queue_.empty();
        }
    public:
        ThreadPool(int num=g_num):num_(num)
        {
            pthread_mutex_init(&mtx_,nullptr);
            pthread_cond_init(&cond_,nullptr);
        }

        static void *Rountine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadPool<T> *tp=(ThreadPool<T> *)args;
            while(true)
            {
                tp->Lock();
                if(tp->IsEmpty())
                {
                    tp->Wait();
                }
                T t;
                tp->PopTask(&t);

                tp->Unlock();

                t.run();
                // std::cout<<"thread running..."<<std::endl;
                // sleep(1);
            }
        }

        void InitThreadPool
        {
            pthread_t tid;
            for(int i=0;i<num_;i++)
            {
                pthread_create(&tid,nullptr,Rountine,(void*)this);
            }
        }

        void PushTask(const T& in)
        {
            Lock();
            task_queue_.push(in);
            Unlock();
            Wakeup();
        }

        void PopTask(T* out)
        {
            *out=task_queue_.front();
            task_queue_.pop();
        }

        ~ThreadPool()
        {
            pthred_mutex_destroy(&mtx_);
            pthread_cond_destroy(&cond_);
        }

    };
}
